/*
 * DIM-SUM操作系统 -- CPU初始化相关文件
 *
 * Copyright (C) 2023 国科础石(重庆)软件有限公司
 *
 * 作者: Dong Peng <w-pengdong@kernelsoft.com>
 *
 * License terms: GNU General Public License (GPL) version 3
 *
 */

#include <dim-sum/init.h>
#include <dim-sum/types.h>
#include <dim-sum/boot_allotter.h>
#include <dim-sum/delay.h>
#include <dim-sum/errno.h>
#include <dim-sum/psci.h>
#include <dim-sum/devtree.h>
#include <dim-sum/cache.h>
#include <asm/processor.h>
#include <asm/sbi.h>
#include <asm/asm-offsets.h>
#include <dim-sum/sched.h>
#include <dim-sum/mem.h>
#include <dim-sum/beehive.h>
#include <dim-sum/smp.h>

extern union process_union *idle_proc_stacks[];
extern struct task_desc *idle_task_desc[];

asmlinkage void ret_from_fork(void) __asm__("ret_from_fork");

extern void task_entry(void);

void *__cpu_up_stack_pointer[MAX_CPUS];
void *__cpu_up_task_pointer[MAX_CPUS];

void (*pm_power_off)(void) = NULL;


static void prepar_secondary_cpubootdata(unsigned int cpuid,
				   struct task_desc *tidle)
{
	int hartid = cpuid_to_hartid_map(cpuid);

	/* Make sure tidle is updated */
	smp_mb();
	WRITE_ONCE(__cpu_up_stack_pointer[hartid],
		   (void*)((unsigned long)tidle->stack + THREAD_START_SP));
	WRITE_ONCE(__cpu_up_task_pointer[hartid], (void*)tidle);
}

static int sbi_hsm_hart_start(unsigned long hartid, unsigned long saddr,
			      unsigned long priv)
{
	struct sbiret ret;

	ret = sbi_ecall(SBI_EXT_HSM, SBI_EXT_HSM_HART_START,
			hartid, saddr, priv, 0, 0, 0);
	if (ret.error)
		return sbi_err_map_linux_errno(ret.error);
	else
		return 0;
}

static int sbi_cpu_start(unsigned int cpuid, struct task_desc *tidle)
{
	int rc;
	unsigned long boot_addr = linear_virt_to_phys(slave_cpu_entry);
	int hartid = cpuid_to_hartid_map(cpuid);

	prepar_secondary_cpubootdata(cpuid, tidle);
	rc = sbi_hsm_hart_start(hartid, boot_addr, 0);

	return rc;
}

int arch_launch_cpu(unsigned int cpu, void* p)
{
	return sbi_cpu_start(cpu, (struct task_desc*)p);
}

// void arch_raise_ipi(const struct cpumask *mask, enum ipi_cmd_type cmd_type)
// {
// 	__smp_raise_ipi(mask, cmd_type);
// }

/**
 * 设置CPU相关选项
 * 例如探测CPU功能
 * 应用补丁等等
 */
void __init init_arch_cpu(void)
{
	/**
	 * 根据CPU特性，确定最终使用何种汇编指令
	 * 来实现特定的功能
	 * 相当于是给CPU打上动态补丁
	 *
	 * 不过暂时RISCV CPU不需要
	 */
	//apply_alternatives_all();
}

void arch_restart(char *cmd)
{
	mdelay(10000);
	BUG();
}

void arch_power_off(void)
{
	mdelay(10000);
	BUG();
}

void arch_init_fork_user_regs(struct task_desc * tsk, unsigned long stack, unsigned long user_stack, struct exception_spot* spot)
{
	tsk->user_regs = (struct exception_spot*)(stack + THREAD_START_SP - S_FRAME_SIZE);
	memcpy(tsk->user_regs, spot, sizeof(struct exception_spot));
	tsk->user_regs->a0 = 0;
	if (user_stack != 0)
		tsk->user_regs->sp = user_stack;
}

void arch_init_fork_kernel_regs(struct task_desc * tsk, unsigned long stack, struct exception_spot* spot)
{
	BUG_ON(!tsk->user_regs);
	memset(&tsk->task_spot, 0x0, sizeof(struct task_spot));

	argv_reg(tsk) = 0;
	envp_reg(tsk)= 0;
	fp_reg(tsk) = 0;
	tsk->task_spot.cpu_context.sp = (unsigned long)tsk->user_regs;
	tsk->task_spot.cpu_context.pc = (unsigned long)(&ret_from_fork);
	// tsk->task_spot.cpu_context.x19 = 0;
	// tsk->task_spot.cpu_context.x20 = 0;
	// tsk->task_spot.cpu_context.fp = 0;
	/**
	 * 这里的魔数 0x10,是因为 __switch_cpu_context 在返回时
	 *	0xffffffc0000a99f0 <__switch_cpu_context+92>:	1f 20 03 d5	nop
	 * 	0xffffffc0000a99f4 <__switch_cpu_context+96>:	ff 43 00 91	add	sp, sp, #0x10
	 * 	0xffffffc0000a99f8 <__switch_cpu_context+100>:	c0 03 5f d6	ret
	 * 存在一个 add	sp, sp, #0x10 的动作。
	 * 为了保证在 __switch_cpu_context 切到 ret_from_fork 时是正常的,这里提前加 0x10
	 */
}

void arch_exit_fork_regs(struct task_desc * tsk)
{

}

void arch_init_kernel_regs(struct task_desc * tsk, unsigned long stack, unsigned long argv, unsigned long envp)
{
	memset(&tsk->task_spot, 0x0, sizeof(struct task_spot));
	argv_reg(tsk) = argv;
	envp_reg(tsk)= envp;
	fp_reg(tsk) = 0;
	tsk->task_spot.cpu_context.sp =  ((unsigned long)stack + THREAD_START_SP);
	tsk->task_spot.cpu_context.pc = (unsigned long)(&task_entry);
}

void arch_init_user_regs(struct task_desc * tsk, unsigned long stack)
{
	tsk->user_regs = (struct exception_spot*)(stack + THREAD_START_SP - S_FRAME_SIZE);
}

char *arch_get_argv(void)
{
	return (char*)argv_reg(current);
}

char *arch_get_envp(void)
{
	return (char*)envp_reg(current);
}

int riscv_dt_processor_hart(struct device_node *device)
{
	const char *isa, *status;
	u32 hart;

	if (!is_device_compatible(device, "riscv")) {
		pr_warn("Found incompatible CPU\n");
		return -ENODEV;
	}

	if (dt_read_u32(device, "reg", &hart)) {
		pr_warn("Found CPU without hart ID\n");
		return -ENODEV;
	}
	if (hart >= MAX_CPUS) {
		pr_info("Found hart ID %d, which is above NR_CPUs.  Disabling this hart\n", hart);
		return -ENODEV;
	}

	if (dt_read_string(device, "status", &status)) {
		pr_warn("CPU with hartid=%d has no \"status\" property\n", hart);
		return -ENODEV;
	}
	if (strcmp(status, "okay")) {
		pr_info("CPU with hartid=%d has a non-okay status of \"%s\"\n", hart, status);
		return -ENODEV;
	}

	if (dt_read_string(device, "riscv,isa", &isa)) {
		pr_warn("CPU with hartid=%d has no \"riscv,isa\" property\n", hart);
		return -ENODEV;
	}
	if (isa[0] != 'r' || isa[1] != 'v') {
		pr_warn("CPU with hartid=%d has an invalid ISA of \"%s\"\n", hart, isa);
		return -ENODEV;
	}

	return (int)hart;
}


/**
 * 调度模块早期初始化
 * 主要是初始化所有核上面的idle堆栈
 */
void init_sched_early(void)
{
	union process_union *stack;
	struct task_desc *proc;
	int i;
	for (i = 0; i < MAX_CPUS; i++)
	{
		if (i == 0)
		{
			idle_proc_stacks[i] = (union process_union *)current_proc_info();
			set_tp_reg((unsigned long)idle_proc_stacks[i]);
		}
		else
		{
			idle_proc_stacks[i] = alloc_boot_mem_permanent(PROCESS_STACK_SIZE,
									PROCESS_STACK_SIZE);

		}

		idle_task_desc[i] = alloc_boot_mem_permanent(sizeof(struct task_desc),
									cache_line_size());
		
		stack = idle_proc_stacks[i];
		proc = idle_task_desc[i];

		init_idle_process(stack, proc, i);

	}
}
